package com.jumper.study.admin.service.impl;


import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.jumper.study.admin.dto.req.OrderSearchReq;
import com.jumper.study.admin.dto.resp.ChartData;
import com.jumper.study.admin.service.OrderService;
import com.jumper.study.admin.service.StudentLessonService;
import com.jumper.study.common.api.OrderStatusCode;
import com.jumper.study.common.api.ResultCode;
import com.jumper.study.common.api.SortField;
import com.jumper.study.common.api.SortType;
import com.jumper.study.common.exception.Asserts;
import com.jumper.study.db.dao.OrderMapper;
import com.jumper.study.db.dao.OrderOperateMapper;
import com.jumper.study.db.dao.OrderStatusMapper;
import com.jumper.study.db.dto.DateCountDto;
import com.jumper.study.db.dto.OrderDto;
import com.jumper.study.db.dto.OrderSearchDto;
import com.jumper.study.db.model.Order;
import com.jumper.study.db.model.OrderOperate;
import com.jumper.study.db.model.OrderStatus;
import com.jumper.study.db.model.StudentLesson;

import cn.hutool.core.util.StrUtil;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private StudentLessonService studentLessonService;

    @Autowired
    private OrderOperateMapper operateMapper;

    @Autowired
    private OrderStatusMapper statusMapper;

    @Override
    public IPage<OrderDto> pageSearchWithAccountAndLesson(IPage<OrderDto> page, OrderSearchReq req) {
        OrderSearchDto search = new OrderSearchDto();
        search.setOrderNo(req.getOrderNo());
        search.setStudentName(req.getStudentName());
        search.setLessonTitle(req.getLessonTitle());
        search.setMinPrice(req.getMinPrice());
        search.setMaxPrice(req.getMaxPrice());
        search.setStartDate(req.getStartDate());
        search.setEndDate(req.getEndDate());
        search.setStatus(req.getStatus());
        search.setSort(new SortField("id", SortType.DESC));

        return getBaseMapper().pageSearchWithAccountAndLesson(page, search);
    }

    @Override
    public OrderDto getWithAccountAndLessonById(Long id) {
        return getBaseMapper().getWithAccountAndLesson(id);
    }

    @Override
    @Transactional
    public boolean updatePrice(Order entity, Integer newPrice, Long adminId) {
        OrderOperate operate = new OrderOperate();
        operate.setAdminId(adminId);
        operate.setOrderId(entity.getId());
        operate.setOperate("修改价格");
        operate.setDescription(StrUtil.format("修改订单，原价格:{}, 新价格:{}", entity.getPrice(), newPrice));

        entity.setPrice(newPrice);
        boolean result = updateById(entity);
        if (!result) {
            Asserts.fail("价格修改失败");
        }

        Integer ret = operateMapper.insert(operate);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_PRICE_FAILED);
        }

        return SqlHelper.retBool(ret);
    }

    @Override
    @Transactional
    public boolean complete(Order entity, Long adminId) {
        if (!entity.canOpenLesson()) {
            Asserts.fail(ResultCode.ORDER_COMPLETE_FAILED);
        }

        OrderOperate operate = new OrderOperate();
        operate.setAdminId(adminId);
        operate.setOrderId(entity.getId());
        operate.setOperate("开通订单课程");
        operate.setDescription("订单通过审核并开通课程");
        Integer ret = operateMapper.insert(operate);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_COMPLETE_FAILED);
        }

        OrderStatus status = new OrderStatus();
        status.setAdminId(adminId);
        status.setOrderId(entity.getId());
        status.setPreStatus(entity.getStatus());
        status.setStatus(OrderStatusCode.COMPLETE);
        status.setOperateId(operate.getId());
        ret = statusMapper.insert(status);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_COMPLETE_FAILED);
        }

        //开通课程
        StudentLesson sLesson = new StudentLesson();
        sLesson.setAccountId(entity.getAccountId());
        sLesson.setLessonId(entity.getLessonId());
        sLesson.setOrderId(entity.getId());
        sLesson.setAdminId(adminId);
        boolean result = studentLessonService.openLesson(sLesson);
        if (!result) {
            Asserts.fail(ResultCode.ORDER_COMPLETE_FAILED);
        }

        entity.setStatus(OrderStatusCode.COMPLETE);
        ret = getBaseMapper().updateById(entity);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_COMPLETE_FAILED);
        }

        return SqlHelper.retBool(ret);
    }

    @Override
    public boolean cancel(Order entity, Long adminId) {
        OrderOperate operate = new OrderOperate();
        operate.setAdminId(adminId);
        operate.setOrderId(entity.getId());
        operate.setOperate("取消订单");
        operate.setDescription("管理员取消订单");
        Integer ret = operateMapper.insert(operate);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_CANCEL_FAILED);
        }

        OrderStatus status = new OrderStatus();
        status.setAdminId(adminId);
        status.setOrderId(entity.getId());
        status.setPreStatus(entity.getStatus());
        status.setStatus(OrderStatusCode.CANCELED);
        status.setOperateId(operate.getId());
        ret = statusMapper.insert(status);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_CANCEL_FAILED);
        }

        //取消订单，如果订单是完成状态，则关闭关联课程
        if (entity.lessonIsOpened()) {
            studentLessonService.closeLesson(entity.getId(), adminId);
        }

        entity.setStatus(OrderStatusCode.CANCELED);
        ret = getBaseMapper().updateById(entity);
        if (!SqlHelper.retBool(ret)) {
            Asserts.fail(ResultCode.ORDER_CANCEL_FAILED);
        }

        return SqlHelper.retBool(ret);
    }

    @Override
    public List<OrderOperate> listOperaters(Long id) {
        LambdaQueryWrapper<OrderOperate> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(OrderOperate::getOrderId, id);
        wrapper.orderByDesc(OrderOperate::getId);

        return operateMapper.selectList(wrapper);
    }

    @Override
    public List<OrderStatus> listStatuses(Long id) {
        LambdaQueryWrapper<OrderStatus> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(OrderStatus::getOrderId, id);
        wrapper.orderByDesc(OrderStatus::getId);

        return statusMapper.selectList(wrapper);
    }

    @Override
    public Long orderCount(LocalDateTime start, LocalDateTime end) {
        LambdaQueryWrapper<Order> query = Wrappers.lambdaQuery();
        if (start != null) {
            query.ge(Order::getCreatedAt, start);
        }
        if (end != null) {
            query.le(Order::getCreatedAt, end);
        }
        return getBaseMapper().selectCount(query);
    }

    @Override
    public ChartData orderCountByDay(LocalDate date, int days) {
        LocalDate start = date.plusDays(-days);
        Map<String, Long> orderMap = getBaseMapper().selectDateCount(
            LocalDateTime.of(start, LocalTime.of(0, 0)),
            LocalDateTime.of(date, LocalTime.of(23, 59, 59))
        ).stream().collect(Collectors.toMap(DateCountDto::getDate, DateCountDto::getCount));

        List<String> categories = new ArrayList<>(days);
        List<Long> orderData = new ArrayList<>(days);
        for(LocalDate d = start; d.isBefore(date); d = d.plusDays(1)) {
            String dateStr = d.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            categories.add(dateStr);
            if (orderMap.containsKey(dateStr)) {
                orderData.add(orderMap.get(dateStr));
            } else {
                orderData.add(0L);
            }
        }

        Map<String, List<Long>> data = new HashMap<>();
        data.put("订单数量", orderData);
        ChartData result = new ChartData();
        result.setCategories(categories);
        result.setDatas(data);
        return result;
    }
    
}
